Skip to content

feat: support S3-compatible storage providers (s3Url, s3ForcePathStyle, caCert)#75

Merged
shubham-pampattiwar merged 9 commits into
migtools:oadp-devfrom
shubham-pampattiwar:issue-23-s3-compatible-support
May 14, 2026
Merged

feat: support S3-compatible storage providers (s3Url, s3ForcePathStyle, caCert)#75
shubham-pampattiwar merged 9 commits into
migtools:oadp-devfrom
shubham-pampattiwar:issue-23-s3-compatible-support

Conversation

@shubham-pampattiwar

@shubham-pampattiwar shubham-pampattiwar commented May 13, 2026

Copy link
Copy Markdown
Member

Summary

Adds support for S3-compatible storage providers (MinIO, NooBaa, Ceph RGW) by wiring four BSL config keys through the full configuration pipeline:

  • s3Url — custom S3 endpoint URL (sets BaseEndpoint on the S3 client)
  • s3ForcePathStyle — path-style addressing (sets UsePathStyle, required by most S3-compatible stores)
  • caCert — PEM-encoded custom CA certificate for private endpoints
  • insecureSkipTLSVerify — skip TLS certificate verification

These keys match the ones supported by Velero's AWS plugin.

Changes

The config flows through four layers, each touched by this PR:

  1. BSL extraction (kubevirt_dataupload_controller.go) — extractBSLConfig() reads the new keys from bsl.Spec.Config
  2. Pod builder (pod_builder.go) — passes values as env vars to the datamover pod
  3. Uploader config (run.go, types.go) — LoadConfigFromEnv() parses the env vars
  4. S3 client creation (objectstore.go) — Init() applies endpoint URL, path-style, and TLS settings

Additionally:

  • Refactored NewS3ObjectStore() to accept a config map instead of individual parameters
  • Simplified InitObjectStore() by delegating credential temp-file handling to Init()
  • TLS client clones http.DefaultTransport to preserve connection pool settings

Backwards Compatibility

All new fields default to zero values. Existing BSL configs without the new keys work exactly as before.

Test Plan

  • Unit tests for extractBSLConfig() — S3 key extraction, case-insensitive booleans, defaults
  • Unit tests for LoadConfigFromEnv() — parsing, defaults, boolean edge cases ("false", "yes")
  • Unit tests for buildDatamoverPod() — env vars set correctly, boolean roundtrip
  • Unit tests for Init() — s3Url, forcePathStyle, caCert, insecureSkipTLSVerify, invalid inputs
  • Unit tests for InitObjectStore() — S3 settings passthrough, unknown provider fallback
  • Unit tests for isValidS3URLScheme() and buildTLSHTTPClient()
  • E2E: deployed MinIO on OCP cluster, configured BSL with s3Url + s3ForcePathStyle + insecureSkipTLSVerify, backup completed successfully with qcow2 uploaded to MinIO

Fixes: #23

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Extended S3-compatible storage support with configurable custom endpoints, path-style addressing modes, TLS certificate verification controls, and custom CA certificates.
  • Tests

    • Added comprehensive test coverage for S3-compatible storage configuration and initialization.

Review Change Stack

shubham-pampattiwar and others added 8 commits May 11, 2026 11:21
Add environment variable constants and UploaderConfig struct fields for
S3-compatible storage provider support: s3Url, s3ForcePathStyle,
insecureSkipTLSVerify, and caCert. These will be wired through the
config pipeline in subsequent commits.

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add S3-compatible storage fields to DatamoverPodConfig and wire them
as environment variables in the datamover pod. Update LoadConfigFromEnv()
to parse the new env vars, including boolean parsing for s3ForcePathStyle
and insecureSkipTLSVerify.

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Read s3Url, s3ForcePathStyle, insecureSkipTLSVerify, and caCert from
bsl.Spec.Config in extractBSLConfig(). Propagate the new fields through
buildDatamoverPodConfig() and initObjectStoreFromBSL() so both the
datamover pod and controller-side object store receive the full config.

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Change NewS3ObjectStore from individual parameters to a config map,
removing the intermediate map construction. Simplify InitObjectStore
by passing credentialsData and S3-compatible settings through the map,
letting Init() handle temp file creation in one place.

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
… S3 client

Wire the S3-compatible BSL config keys into the S3 client creation:
- s3Url sets BaseEndpoint for custom S3 endpoints (MinIO, NooBaa, Ceph)
- s3ForcePathStyle enables UsePathStyle addressing
- caCert configures custom CA certificate for private endpoints
- insecureSkipTLSVerify skips TLS certificate verification

URL scheme is validated (http/https only) and invalid CA PEM is rejected
with a clear error. TLS configuration follows the same approach as
Velero's AWS plugin.

Fixes: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Add tests for coverage gaps identified during review:
- InitObjectStore with S3 settings (configMap building)
- InitObjectStore with unknown provider (S3-compatible fallback)
- Init with insecureSkipTLSVerify alone
- Boolean parsing edge cases ("false", "yes" must not parse as true)
- strconv.FormatBool roundtrip (controller → env var → uploader)

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Use http.DefaultTransport.Clone() instead of a bare http.Transport{}
when building the custom TLS client. This preserves Go's default
connection pooling, timeouts, keep-alive, and proxy settings.

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@openshift-ci openshift-ci Bot requested review from mpryc and weshayutin May 13, 2026 18:04
@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@shubham-pampattiwar has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 56 minutes and 29 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3aae15c2-65cc-4b63-97dd-b0020fc61d4c

📥 Commits

Reviewing files that changed from the base of the PR and between 2f33504 and b3d4a9e.

📒 Files selected for processing (4)
  • internal/controller/kubevirt_dataupload_controller.go
  • internal/controller/pod_builder_test.go
  • pkg/uploader/run.go
  • pkg/uploader/run_test.go
📝 Walkthrough

Walkthrough

This PR extends S3 object-store support to handle S3-compatible storage providers (MinIO, NooBaa, Ceph RGW) by routing additional BackupStorageLocation configuration settings through the datamover pod and into object-store initialization. Configuration for custom endpoints, path-style addressing, and custom CA certificates for TLS now flows from the controller through environment variables to the uploader, enabling S3-compatible backends with proper endpoint and security handling.

Changes

S3-Compatible Configuration Plumbing

Layer / File(s) Summary
Config types and env var constants
pkg/uploader/types.go
New exported environment variable constants (EnvBSLS3URL, EnvBSLS3ForcePathStyle, EnvBSLInsecureSkipTLSVerify, EnvBSLCACert) and corresponding UploaderConfig fields to carry S3-compatible settings through the system.
Controller BSL config extraction
internal/controller/kubevirt_dataupload_controller.go, internal/controller/kubevirt_dataupload_controller_test.go
Extended extractBSLConfig to parse S3-compatible keys (s3Url, s3ForcePathStyle, insecureSkipTLSVerify, caCert) from bsl.Spec.Config and propagate them into both datamover pod config and uploader config initialization, with comprehensive test coverage for extraction, boolean parsing, and default behavior.
Pod environment variable wiring
internal/controller/pod_builder.go, internal/controller/pod_builder_test.go
Extended DatamoverPodConfig struct with S3 fields and updated buildDatamoverPod to set corresponding environment variables for the datamover container, with tests verifying S3 env var presence and boolean roundtrip parsing.
Uploader config loading
pkg/uploader/run.go, pkg/uploader/run_test.go
Updated LoadConfigFromEnv to read new S3-compatible settings from environment variables, with case-insensitive boolean parsing and test coverage for provided values, defaults, and rejection of non-true boolean strings.
Object store S3-compatible support and TLS
pkg/uploader/objectstore.go, pkg/uploader/objectstore_test.go
Refactored NewS3ObjectStore to accept a generic configMap parameter; updated Init to validate S3 URL schemes, apply custom endpoints and path-style addressing, and construct TLS-enabled HTTP clients with custom CA certificates or insecure TLS verification. Added helper functions isValidS3URLScheme and buildTLSHTTPClient, and comprehensive test suite covering URL validation, TLS client construction, and integration with InitObjectStore.

Sequence Diagram

sequenceDiagram
  participant BSL as BackupStorageLocation
  participant Controller as extractBSLConfig
  participant Pod as buildDatamoverPod
  participant Env as LoadConfigFromEnv
  participant Store as S3ObjectStore.Init
  BSL->>Controller: spec.Config (s3Url, s3ForcePathStyle, caCert, insecureSkipTLSVerify)
  Controller->>Pod: bslConfig fields
  Pod->>Env: environment variables (BSLS3URL, etc.)
  Env->>Store: UploaderConfig (BSLS3URL, BSLS3ForcePathStyle, BSLCACert, BSLInsecureSkipTLSVerify)
  Store->>Store: parseS3URL, buildTLSHTTPClient
  Store->>Store: configureBaseEndpoint, enablePathStyle, attachCustomCA
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

Possibly related PRs

  • migtools/kubevirt-datamover-controller#10: Extends the Phase-3 datamover pod and S3 uploader implementation by adding S3-compatible BSL configuration plumbing through the same controller/pod builder/object store code paths.
  • migtools/kubevirt-datamover-controller#13: Both PRs modify the controller's BackupStorageLocation extraction and object-store initialization flow, with this PR layering new S3/TLS field propagation onto the same BSL-driven checkpoint.

Suggested labels

kubevirt-dm, ai-generated-test

Suggested reviewers

  • weshayutin
  • sseago
  • mpryc

Poem

🐰 The rabbit hops with S3 grace,
MinIO and NooBaa find their place,
With custom certs and paths so true,
The datamover dances through!
TLS verified, endpoints shine,
S3-compatible works just fine! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.74% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding support for S3-compatible storage providers with specific configuration keys (s3Url, s3ForcePathStyle, caCert). It is concise, clear, and directly related to the primary objective.
Linked Issues check ✅ Passed All coding requirements from issue #23 are met: BSL config extraction, env var propagation through pod builder and LoadConfigFromEnv, S3 client wiring for endpoints/path-style/TLS/CA, URL scheme validation, PEM validation, and comprehensive unit tests.
Out of Scope Changes check ✅ Passed All changes directly support the issue requirements. File modifications in controller, pod builder, uploader, and tests are within scope and serve the S3-compatible provider support objective.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@shubham-pampattiwar

Copy link
Copy Markdown
Member Author

/cherry-pick oadp-1.6

@openshift-cherrypick-robot

Copy link
Copy Markdown

@shubham-pampattiwar: once the present PR merges, I will cherry-pick it on top of oadp-1.6 in a new PR and assign it to you.

Details

In response to this:

/cherry-pick oadp-1.6

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/uploader/run_test.go (1)

140-384: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reduce TestLoadConfigFromEnv cyclomatic complexity to unblock lint

Line 140 currently fails gocyclo (31 > 30), so this PR cannot pass CI as-is. Please split this test into smaller focused helpers/subtests.

Suggested split
 func TestLoadConfigFromEnv(t *testing.T) {
-    // all cases + all validations in one function
+    t.Run("core required/default config", testLoadConfigFromEnvCore)
+    t.Run("S3-compatible config parsing", testLoadConfigFromEnvS3)
 }
+
+func testLoadConfigFromEnvCore(t *testing.T) {
+    // existing non-S3 cases
+}
+
+func testLoadConfigFromEnvS3(t *testing.T) {
+    // existing S3 URL / forcePath / insecure / caCert cases
+}

As per coding guidelines, "After editing Go controller or other source files, run make lint-fix to auto-fix code style and make test to run unit tests."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/uploader/run_test.go` around lines 140 - 384, The TestLoadConfigFromEnv
function is too complex (gocyclo > 30); refactor by extracting repeated setup
and assertion logic into small helper functions and per-case subtests: create
helpers like setEnvVars(map[string]string) to apply/clear environment,
runLoadConfigExpect(t, name, envVars, expectError, validate) to call
LoadConfigFromEnv and assert error/no-error, and smaller validators (e.g.,
validateDefaults, validateS3Settings) to hold the validation blocks; then
rewrite TestLoadConfigFromEnv to iterate test cases but call those helpers
(referencing TestLoadConfigFromEnv, LoadConfigFromEnv, and the validate
closures) so the main test body is short and each subtest focuses on one
concern, reducing cyclomatic complexity.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/controller/pod_builder_test.go`:
- Around line 347-424: The new test TestS3CompatibleBooleanRoundtrip contains
gofmt/formatting issues; run the formatter (or run make lint-fix) and reformat
this test block so it passes CI; ensure spacing/indentation in the
TestS3CompatibleBooleanRoundtrip function and its use of buildDatamoverPod,
container.Env loop, envMap lookups, and strings.EqualFold parsing (and
references like uploader.EnvBSLS3ForcePathStyle /
uploader.EnvBSLInsecureSkipTLSVerify) are properly formatted according to gofmt
before committing.

---

Outside diff comments:
In `@pkg/uploader/run_test.go`:
- Around line 140-384: The TestLoadConfigFromEnv function is too complex
(gocyclo > 30); refactor by extracting repeated setup and assertion logic into
small helper functions and per-case subtests: create helpers like
setEnvVars(map[string]string) to apply/clear environment, runLoadConfigExpect(t,
name, envVars, expectError, validate) to call LoadConfigFromEnv and assert
error/no-error, and smaller validators (e.g., validateDefaults,
validateS3Settings) to hold the validation blocks; then rewrite
TestLoadConfigFromEnv to iterate test cases but call those helpers (referencing
TestLoadConfigFromEnv, LoadConfigFromEnv, and the validate closures) so the main
test body is short and each subtest focuses on one concern, reducing cyclomatic
complexity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: cb325027-6f49-41cb-b292-5afecfd5ab1d

📥 Commits

Reviewing files that changed from the base of the PR and between d2e2846 and 2f33504.

📒 Files selected for processing (9)
  • internal/controller/kubevirt_dataupload_controller.go
  • internal/controller/kubevirt_dataupload_controller_test.go
  • internal/controller/pod_builder.go
  • internal/controller/pod_builder_test.go
  • pkg/uploader/objectstore.go
  • pkg/uploader/objectstore_test.go
  • pkg/uploader/run.go
  • pkg/uploader/run_test.go
  • pkg/uploader/types.go

Comment thread internal/controller/pod_builder_test.go
Run gofmt on files with alignment issues and add nolint:gocyclo
directive to TestLoadConfigFromEnv (complexity 31, threshold 30)
matching the existing pattern in TestBuildDatamoverPod.

Refs: migtools#23

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@openshift-ci

openshift-ci Bot commented May 14, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: shubham-pampattiwar, sseago, weshayutin

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:
  • OWNERS [shubham-pampattiwar,sseago,weshayutin]

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@shubham-pampattiwar shubham-pampattiwar merged commit a4ea95b into migtools:oadp-dev May 14, 2026
5 of 6 checks passed
@openshift-cherrypick-robot

Copy link
Copy Markdown

@shubham-pampattiwar: new pull request created: #76

Details

In response to this:

/cherry-pick oadp-1.6

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feat: Support S3-compatible storage providers (s3Url, s3ForcePathStyle, caCert)

4 participants